package com.george.order.loadbalance;


import java.util.Arrays;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * @title: ConsistentHashingLoadBalancer.java
 * @description: 负载均衡算法-一致性哈希（Consistent Hashing）
 * @author: George.Dong
 * @date: 2024/5/31 9:55
 */
public class ConsistentHashingLoadBalancer {
    // 存储哈希环，使用TreeMap保证键的有序性
    private static TreeMap<Integer, String> circle = new TreeMap<>();
    // 每个服务器对应的虚拟节点数量，即每个服务器在哈希环上代表的虚拟节点数量
    private static int numberOfReplicas = 160;

    /**
     * 构造函数，初始化负载均衡器。
     *
     * @param servers 服务器列表
     */
    public ConsistentHashingLoadBalancer(List<String> servers) {
        // 为每个服务器添加指定数量的虚拟节点到哈希环
        for (String server : servers) {
            // 将真实节点的IP映射到哈希环上
            circle.put(getHashCode(server), server);
            // 根据设定的虚拟节点数量进行虚拟节点映射
            for (int i = 0; i < numberOfReplicas; i++) {
                // 计算出一个虚拟节点的哈希值（只要不同即可）
                int hash = getHashCode(server + i);
                // 将虚拟节点添加到哈希环结构上
                circle.put(hash, server);
            }
        }
    }


    /**
     * 获取服务器
     *
     * @param ip ip
     * @return {@link String}
     */
    public static String getServer(String ip) {
        // 计算一个IP的哈希值
        int hashCode = getHashCode(ip);
        // 得到大于该Hash值的子红黑树
        SortedMap<Integer, String> sortedMap = circle.tailMap(hashCode);
        // 得到该树的第一个元素，也就是最小的元素(即是在命中的服务器区间取最小的元素，如Server1的最小哈希元素)
        Integer treeNodeKey = sortedMap.firstKey();
        // 如果没有大于该元素的子树了，则取整棵树的第一个元素，相当于取哈希环中的最小元素(即是所有服务器中挑选一个哈希最小的)
        if (sortedMap == null) {
            treeNodeKey = sortedMap.firstKey();
        }
        // 返回对应的虚拟节点名称
        return circle.get(treeNodeKey);
    }


    /**
     * 哈希方法：用于计算一个IP的哈希值
     *
     * @param ip IP
     * @return int
     */
    public static int getHashCode(String ip) {
        final int p = 1904390101;
        int hash = (int) 1901102097L;
        for (int i = 0; i < ip.length(); i++) {
            hash = (hash ^ ip.charAt(i)) * p;
        }
        hash += hash << 13;
        hash ^= hash >> 7;
        hash += hash << 3;
        hash ^= hash >> 17;
        hash += hash << 5;

        // 如果算出来的值为负数则取其绝对值
        if (hash < 0)
            hash = Math.abs(hash);
        return hash;
    }

    public static void main(String[] args) {
        List<String> servers = Arrays.asList("Server1", "Server2", "Server3");
        ConsistentHashingLoadBalancer lb = new ConsistentHashingLoadBalancer(servers);

        System.out.println("-----------不同ip访问------------------");
        // 用for循环模拟五个不同的IP访问
        for (int i = 1; i <= 5; i++) {
            String clientIp = "192.168.0.13" + i;
            System.out.println("Client IP: " + clientIp + " -> Server: " + lb.getServer(clientIp));
        }
        System.out.println("-----------相同ip访问------------------");
        // 用for循环模拟三个相同的IP访问
        for (int i = 1; i <= 3; i++) {
            String clientIp = "192.168.0.131";
            System.out.println("Client IP: " + clientIp + " -> Server: " + lb.getServer(clientIp));
        }
    }
}
